Mem3.c中主要涉及到两个结构体,这里介绍一下第一个比较重要的结构体Mem3Block,该结构体定义如下:
typedef struct Mem3Block Mem3Block;
struct Mem3Block {
union {
struct {
u32 prevSize; /* Size of previous chunk in Mem3Block elements */
u32 size4x; /* 4x the size of current chunk in Mem3Block elements */
} hdr;
struct {
u32 next; /* Index in mem3.aPool[] of next free chunk */
u32 prev; /* Index in mem3.aPool[] of previous free chunk */
} list;
} u;
};
前面已经提到过,一个chunk有两个或两个以上的block组成,这个结构体就是用来形成chunk链表的。可以看到,第一个block有8个字节,其中,前四个字节(u32 prevSive)用来存储链表中前一个chunk块的大小,后四个字节(u32 size4x)用来存储当前块大小的四倍,为什么size4x是当前chunk大小的四倍呢?原因是最后一个bit和倒数第二个bit有特殊的含义,最后一个bit是否为1, 用来保存当前块是否已经分配出去了,倒数第二个bit 是否为1 上一个块是否被分配出去了。除此之外的另外一个结构体也是很重要的,它定义了整个mem3.c中用到的全局变量,这里就不再具体介绍了。
下面是两个关于内存分配的实现函数部分代码:
实现内存分配:
static void *memsys3FromMaster(u32 nBlock){
assert( sqlite3_mutex_held(mem3.mutex) );
assert( mem3.szMaster>=nBlock );
if( nBlock>=mem3.szMaster-1 ){
/* Use the entire master */ 使用整个master
void *p = memsys3Checkout(mem3.iMaster, mem3.szMaster);
mem3.iMaster = 0;
mem3.szMaster = 0;
mem3.mnMaster = 0;
return p;
}else{
/* Split the master block. Return the tail. */切割master进行分配
u32 newi, x;
newi = mem3.iMaster + mem3.szMaster - nBlock;
assert( newi > mem3.iMaster+1 );
mem3.aPool[mem3.iMaster+mem3.szMaster-1].u.hdr.prevSize = nBlock;
mem3.aPool[mem3.iMaster+mem3.szMaster-1].u.hdr.size4x |= 2;
mem3.aPool[newi-1].u.hdr.size4x = nBlock*4 + 1;
mem3.szMaster -= nBlock;
mem3.aPool[newi-1].u.hdr.prevSize = mem3.szMaster;
x = mem3.aPool[mem3.iMaster-1].u.hdr.size4x & 2;
mem3.aPool[mem3.iMaster-1].u.hdr.size4x = mem3.szMaster*4 | x;
if( mem3.szMaster < mem3.mnMaster ){
mem3.mnMaster = mem3.szMaster;
}
return (void*)&mem3.aPool[newi];
}
}
这个函数主要实现的功能是内存分配。Mem3.c又叫实验性内存分配器,在系统初始化前就分配了一块内存,之后这块内存就被固定下来。这块固定的内存就是master,每一次用户需要使用内存时,最开始都要从master上切割对应大小的块,上面这个函数实现的功能就是从master上分配内存的过程。不管什么时候,总保证master是最大的块。用过后的块根据其大小,连接成空闲块链表,用户需要时直接从空闲双链表中取。
因此就涉及到对双向链表的删除和插入操作,其中,删除对应的真正意义是,该块被用户申请走了,被利用了,就要将其拿出链表。对应实现函数代码如下:
static void memsys3UnlinkFromList(u32 i, u32 *pRoot){
u32 next = mem3.aPool[i].u.list.next;
u32 prev = mem3.aPool[i].u.list.prev;
assert( sqlite3_mutex_held(mem3.mutex) );
if( prev==0 ){
*pRoot = next;
}else{
mem3.aPool[prev].u.list.next = next;
}
if( next ){
mem3.aPool[next].u.list.prev = prev;
}
mem3.aPool[i].u.list.next = 0;
mem3.aPool[i].u.list.prev = 0;
}
同样,插入就意味着用户使用完内存后,要将其插入到空闲链表,这样就防止了内存泄漏问题,具体实现代码如下:
static void memsys3LinkIntoList(u32 i, u32 *pRoot){
assert( sqlite3_mutex_held(mem3.mutex) );
mem3.aPool[i].u.list.next = *pRoot;
mem3.aPool[i].u.list.prev = 0;
if( *pRoot ){
mem3.aPool[*pRoot].u.list.prev = i;
}
*pRoot = i;
}
除了以上几个主要函数外,其他函数代码我就不继续介绍了,在源码阅读里面已经有详细注释。